feat: auto-generate session titles via the managed chat_title tool - #2351
Merged
Conversation
With the auto-title experimental flag on and a managed OAuth login, the
session title is generated from the first prompt, replacing the
truncated-prompt easy title. A custom title set by the user is never
overwritten, and generation failures degrade silently to the easy title.
- oauth: fetchChatTitle for the platform /tools chat_title method
- agent-core (v1): fire-and-forget generation on the first prompt
- agent-core-v2: sessionTitle domain watching the easy-title event
- kap-server: POST /sessions/{id}/title/generate for manual regeneration
🦋 Changeset detectedLatest commit: e2a7e5c The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Drop the automatic wiring on both engines: the v1 (TUI) first-prompt trigger and the v2 easy-title event watcher. SessionTitleService's generateTitle() stays as the single on-demand entry point behind the auto-title flag, backing the kap-server title/generate route. The changeset goes away too: with no shipped consumer, the remaining surface is not user-perceivable.
commit: |
Record up to three sanitized natural-language prompts in session metadata (skill / plugin activations excluded) and compose the chat_title input as order-labeled lines truncated to a 1000-char budget, falling back to lastPrompt for sessions without recorded prompts.
# Conflicts: # packages/agent-core-v2/test/session/sessionMetadata/sessionMetadata.test.ts
- persist titleSource (prompt/generated/custom); skip auto-generation over an already-generated title unless forced, and never over a custom one - plumb the force option from the core through klient and node-sdk to the REST title/generate endpoint - drop the title write-back when the session scope was superseded mid-flight, and retry once with a force-refreshed token on a 401 - stop closing sessions a concurrent public resume has handed out in the temporary resume paths (generateSessionTitle, renameSession) - accept session.meta.updated patches without lastPrompt in klient event validation, and emit exactly one metadata event per applied title - remove the retired prompts field heal and drop the changeset (the behavior is only perceivable on the experimental v2 engine)
# Conflicts: # packages/agent-core-v2/docs/state-manifest.d.ts # packages/agent-core-v2/test/workspace/workspaceHandler/workspaceHandler.test.ts # packages/node-sdk/src/sdk-rpc-client-v2.ts
…dler The session title service must not depend on the Workspace-tier handler registry. The handler now seeds each session scope with an abort signal, fires it synchronously when a close begins, and the title service carries the signal on its request, drops the write-back once aborted, and drains an in-flight generation through the onWillCloseSession hook.
A v1 rename spreads the original state.json document, so an explicit isCustomTitle: true can travel with a stale titleKind. The explicit marker now wins on load, and every persist double-writes the derived isCustomTitle so released v1 builds keep recognizing the custom title.
The temporary resume/rename/close paths and the public lifecycle operations now share a per-session queue, so a public resume can never receive a handle whose cleanup close is already in flight. Session summaries carry the canonical title state, letting the TUI skip title generation for sessions whose title was already generated or customized instead of re-asking after every turn.
A close/archive is now tracked in a closing registry from its first synchronous step until disposal: get/list hide the closing session and resume waits the close out instead of returning the doomed handle, and fork waits out an in-flight source close. The title service tracks the whole generateTitle call as the unit the close hook drains, and the generated-title write re-checks the lifetime signal inside the serialized metadata update so an abort landing while the update is queued still vetoes the write-back.
readSummary and the read-model mirror carry titleKind, so listSessions reports the same canonical title state as a resumed session's summary.
Nothing calls force: with it gone, plain calls always coalesce onto the shared in-flight generation, so the generation sequence and the caller-supplied allowWhen veto lose their only purpose and go with it. The title/generate REST route takes no body anymore.
…sion index The listed-session titleKind had no consumer: the TUI's title-generation gate seeds from the resumed summary, which reads the live metadata document, and the kap-server REST wire never carried the field. Removing the projection also retires the read-model summary version stamp (the remaining shape is fully field-checkable) and the duplicate title-kind derivation that had to stay in lockstep with sessionMetadata. The klient list contract and the node-sdk list mapper drop the field with it; the resumed/live summary still reports the canonical title state.
…essageLegacy mergeContextTranscriptWithLive had a single caller; move the logic into messageLegacyService as the private mergeLiveTail and drop the export.
# Conflicts: # packages/agent-core-v2/docs/state-manifest.d.ts # packages/agent-core-v2/scripts/check-domain-layers.mjs # packages/agent-core-v2/src/session/sessionMetadata/sessionMetadataService.ts # packages/agent-core-v2/src/workspace/sessionLifecycle/sessionLifecycleService.ts # packages/node-sdk/src/sdk-rpc-client-v2.ts
…ifecycle Auto title no longer consumes close-awareness, so the machinery goes back to the simple forms: close/archive run straight through, resume no longer waits out an in-flight close, create/fork drop their target reservation, and a cold archive is a no-op again. Reverts the behavior of e7c397a and cd1cea0 on top of the sessionLifecycle rename.
Signed-off-by: 7Sageer <sag77r@hotmail.com>
…title test The main merge reduced #/kosong/model/hostRequestHeaders to the pure port contract; define the test's headers as a plain value matching it and tidy the SDK test import grouping.
Keep the capability only: the engine-side title generation service, the
POST /sessions/{id}/title/generate route, and the SDK generateSessionTitle
method stay; the TUI no longer requests a title on prompt accept or on
session attach. The changeset now covers the SDK capability instead of a
CLI-facing auto title.
Signed-off-by: 7Sageer <sag77r@hotmail.com>
ISessionTitleService.generateTitle and the metadata write-back take an
optional force flag that bypasses the custom/generated guards, so an
explicit user request (the desktop/web rename field's Gen Title action)
can overwrite any current title; the applied title is marked generated.
Forced calls skip the in-flight coalescing, and a forced write is plain
last-writer-wins.
kap-server's POST /sessions/{id}/title/generate accepts an optional
{ "force": true } body; klient and the node-sdk plumb the option through
(GenerateSessionTitleInput.force).
Also renumber SESSION_TITLE_UNAVAILABLE to 40923: main assigned 40922 to
PAGE_TOKEN_MISMATCH after this branch forked.
…ation generateTitle gains a source option alongside force: - user_prompts (default): the existing first-prompts window, unchanged. - first_turn: the opening user prompt paired with the first turn's final assistant text — strict, so a caller asking before the first reply lands simply gets unavailable and can retry at the next turn boundary. - digest: first prompt + latest prompt + the latest turn's final assistant text, tolerating a compacted window by using whatever segments survive; meant for explicit regeneration on multi-turn sessions. Assistant segments keep only natural-language text parts (tool calls, thinking, and media never contribute) and pass through the shared metadata sanitizer, which redacts secrets and long base64-looking runs; each segment is capped (user 300, assistant 600/400) so the composed chat_content stays within the 1000-char budget. The kap-server route, klient contract, and node-sdk plumb the option through. Excerpt extraction is covered against the real context memory (loop-event folding), and the REST surface gains a digest composition case.
# Conflicts: # packages/agent-core-v2/src/agent/rpc/prompt-metadata.ts # packages/agent-core-v2/src/index.ts # packages/agent-core-v2/src/session/sessionMetadata/sessionMetadataService.ts # packages/agent-core-v2/src/workspace/sessionLifecycle/sessionLifecycleService.ts # packages/agent-core-v2/test/agent/prompt/promptMetadataText.test.ts # packages/agent-core-v2/test/app/sessionIndex/sessionIndex.test.ts # packages/agent-core-v2/test/workspace/sessionLifecycle/sessionLifecycle.test.ts # packages/kap-server/src/protocol/error-codes.ts # packages/kap-server/src/routes/sessions.ts # packages/klient/src/contract/index.ts # packages/klient/src/transports/memory/serviceRegistry.ts # packages/klient/test/contract-parity.ts # packages/node-sdk/src/sdk-rpc-client-v2.ts # packages/node-sdk/test/sdk-rpc-client-v2.test.ts # packages/node-sdk/test/v1-v2-parity.test.ts
Contributor
|
❌ Nix build failed |
…ntal flag Registers the flag (off by default; env KIMI_CODE_EXPERIMENTAL_SESSION_TITLE, the master flag, or the [experimental] config section) and makes generateTitle report unavailable while it is off, so every entry point — the kap-server route, klient, node-sdk, and through them the clients' auto trigger and rename-field action — is inert unless the user opts in.
Snake-case id matching search_worker / persistence_minidb_readmodel; env KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE.
The main-merge commit captured pre-fix snapshots of four files; the actual resolutions only lived in my working tree: the LifecycleScope import move to #/app/scopes, the titleKind port of applyPromptMetadataUpdate, the Promise<void> pinning of the metadata update queue, and the reloadSession runSessionAccess closure.
Generation now reports unavailable with the flag off, so the two title-generation harnesses opt in through the written config's [experimental] section.
Merged
mohidmakhdoomi
added a commit
to mohidmakhdoomi/kimi-code
that referenced
this pull request
Aug 13, 2026
Two files conflicted, both where main rewrote a region this branch also touches: - packages/node-sdk/src/sdk-rpc-client-v2.ts — the engine import block, where MoonshotAI#2871 replaced the agent RPC aggregation layer with IAgentPluginCommandService and MoonshotAI#2700 removed IModelCatalog along with the secondary-model machinery. Main's removals are kept and this branch's additions re-applied on top of them; no removed service is resurrected. - docs/zh/reference/kimi-command.md — main unified the locale on "main agent" and "subagent", so the --agent-file paragraph this branch adds is restated in that terminology. One comment was rewritten rather than merged as-is. The rollback in createSession justified its handle-identity check with the SESSION_ALREADY_EXISTS check not being atomic, which MoonshotAI#2351 has since made false: an explicit id now runs the whole create behind a per-session queue. The check itself stays, because the engine's create still registers its handle without a duplicate-id guard of its own, but it is now described as what keeps a rollback from taking a session the call did not create. The claims the rest of the change rests on were re-verified against the merged tree: session creation still awaits the same five workspace profile loaders, still announces the finished session as its last step, and MoonshotAI#2896's move of the session lifecycle hooks onto lifecycle events leaves SessionStart and SessionEnd firing from create and close as before.
7723qqq
added a commit
to 7723qqq/kimi-code
that referenced
this pull request
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
No linked issue — the problem is explained below.
Problem
Session titles are initially derived by truncating the first user prompt to 200 characters. Long or pasted prompts produce unreadable sidebar titles, while the managed platform already exposes a
chat_titletool that can generate a concise title from conversation content. The server stack had no reusable on-demand path for calling that tool and persisting the result.What changed
Adds automatic session title generation for agent-core-v2 sessions authenticated through the managed Kimi Code OAuth provider. As soon as the engine accepts a prompt, the TUI asks it to generate a title from the conversation's first prompts, retrying after each main-agent turn ends until one is applied; a title is generated at most once per session unless explicitly regenerated. This takes effect only on the v2 engine: the default v1 CLI keeps prompt-derived titles, and
kimi webdoes not trigger generation yet.Generation requires a managed OAuth login and otherwise leaves the current title untouched. For logged-in users, up to the first three natural-language user prompts (each prefixed with a plain
user:role marker, capped at 1000 characters combined) are sent to the managed/toolschat_titleendpoint — this applies to sessions on any model provider, consistent with the other managed tools (web search, fetch-url). Sessions without a managed login are unaffected.fetchChatTitlefor the managed platform/toolschat_titlemethod, including authoritative protocol headers, an 8-second timeout, response validation, and structured failures.ISessionTitleServiceand Agent-scoped prompt source. The service coalesces concurrent requests, caps generated titles at 200 characters, retries once with a force-refreshed token on a 401, and drops the write-back when the session scope was closed or superseded mid-flight. Persisted metadata records the title source (prompt/generated/custom): a custom title is never overwritten, and an already-generated title is not regenerated unless forced.POST /api/v1/sessions/{session_id}/title/generateaccepts an optional{ "force": true }body to regenerate an already-generated title. Unknown sessions return40401; unavailable generation returns40922.forceoption through the public facade; fixsession.meta.updatedpatches withoutlastPromptbeing dropped by klient event validation; emit exactly one metadata event per applied title; the temporary resume→act→close paths (generateSessionTitle,renameSession) no longer close a session that a concurrent public resume has handed out.Checklist
gen-changesetsskill;.changeset/auto-session-title.mdannounces the automatic title as aminorCLI entry.gen-docsskill; no user documentation update is needed until a shipped surface exposes this behavior.